package crontab

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/robfig/cron/v3"
	types2 "github.com/wsw188440873/ctrl-cali/types"
	"iot-base/auth/svc"
	"iot-base/auth/utils"
	"strconv"
	"strings"
	"sync"
	"time"
)

// 字段名	是否必须	允许的值	允许的特定字符
// 秒(Seconds)	是	0-59	* / , -
// 分(Minutes)	是	0-59	* / , -
// 时(Hours)	是	0-23	* / , -
// 日(Day of month)	是	1-31	* / , – ?
// 月(Month)	是	1-12 or JAN-DEC	* / , -
// 星期(Day of week)	否	0-6 or SUM-SAT	* / , – ?

type cronTask struct {
	cron    *cron.Cron
	taskMap map[string]cron.EntryID
	m       sync.Mutex
}

var Cron cronTask

func init() {
	Cron.start()
}

func (t *cronTask) start() {
	t.cron = newCron()
	t.taskMap = make(map[string]cron.EntryID)
	t.cron.Start()
}

func newCron() *cron.Cron {
	secondParser := cron.NewParser(cron.Second | cron.Minute |
		cron.Hour | cron.Dom | cron.Month | cron.DowOptional | cron.Descriptor)
	return cron.New(cron.WithParser(secondParser), cron.WithChain())
}

func (t *cronTask) AddTask(id int64, taskType string, datetime time.Time, day int) error {
	spec := t.fmtTaskSpec(strings.Trim(taskType, " "), datetime, day)
	//fmt.Println("fmtTaskSpec: ", spec)
	EntryID, err := t.cron.AddFunc(spec, func() { runCaliTask(id) })
	t.m.Lock()
	defer t.m.Unlock()
	t.taskMap[strconv.FormatInt(id, 10)] = EntryID
	return err
}

// AddSubTask 添加子任务.
func (t *cronTask) AddSubTask(id int64, taskLogId string, params map[string]string) error {
	execTime, ok := params["exec_time"]
	if !ok {
		return errors.New("exec_time not found")
	}
	groupId, ok := params["group_id"]
	if !ok {
		return errors.New("group_id not found")
	}
	//utils.AutoID()
	dateTime, err := utils.StringToTime(execTime)
	if err != nil {
		return err
	}

	// 如果执行时间小于当前时间，就不用执行了
	if dateTime.Unix() < time.Now().Unix() {
		return errors.New("exec_time err")
	}
	// 把标准时间转换成定时任务的格式.
	var seconds, minutes, hours, day, month, week = "*", "*", "*", "*", "*", "*"
	month = strconv.Itoa(int(dateTime.Month()))
	day = strconv.Itoa(dateTime.Day())
	hours = strconv.Itoa(dateTime.Hour())
	minutes = strconv.Itoa(dateTime.Minute())
	seconds = strconv.Itoa(dateTime.Second())
	spec := fmt.Sprintf("%v %v %v %v %v %v", seconds, minutes, hours, day, month, week)
	EntryID, err := t.cron.AddFunc(spec, func() { runCaliSubTask(id, taskLogId, groupId, execTime) })
	t.m.Lock()
	defer t.m.Unlock()
	t.taskMap[taskLogId+groupId] = EntryID
	return err
}

//fmtTaskSpec 格式化.
func (t *cronTask) fmtTaskSpec(taskType string, datetime time.Time, num int) string {
	var seconds, minutes, hours, day, month, week = "*", "*", "*", "*", "*", "*"
	switch taskType {
	case "day":
		seconds = strconv.Itoa(datetime.Second())
		minutes = strconv.Itoa(datetime.Minute())
		hours = strconv.Itoa(datetime.Hour())
		break
	case "week":
		seconds = strconv.Itoa(datetime.Second())
		minutes = strconv.Itoa(datetime.Minute())
		hours = strconv.Itoa(datetime.Hour())
		week = strconv.Itoa(num)
		break
	case "month":
		seconds = strconv.Itoa(datetime.Second())
		minutes = strconv.Itoa(datetime.Minute())
		hours = strconv.Itoa(datetime.Hour())
		day = strconv.Itoa(num)
		break
	}
	spec := fmt.Sprintf("%v %v %v %v %v %v", seconds, minutes, hours, day, month, week)
	return spec
}

func (t *cronTask) RemoveTask(id int64) {
	t.m.Lock()
	defer t.m.Unlock()
	idStr := strconv.FormatInt(id, 10)
	if v, ok := t.taskMap[idStr]; ok {
		t.cron.Remove(v)
		delete(t.taskMap, idStr)
	}
}

func (t *cronTask) RemoveTaskStr(id string) {
	t.m.Lock()
	defer t.m.Unlock()
	if v, ok := t.taskMap[id]; ok {
		t.cron.Remove(v)
		delete(t.taskMap, id)
	}
}

func ReloadTasks() {
	tasks, err := svc.Dao.TTasks.FindAll()
	if err != nil || len(tasks) == 0 {
		return
	}
	// 这个任务比较特殊，如果有子任务，需要重新加载
	for index := range tasks {
		if tasks[index].TaskType == types2.ZeroDriftAndSpanDrift {
			b, err := tasks[index].SubTaskParams.MarshalJSON()
			if err != nil {
				return
			}
			// test
			data := make([]map[string]string, 0, 7)
			err = json.Unmarshal(b, &data)
			if err != nil {
				return
			}
			ZeroDriftAndSpanDriftSubTask(tasks[index].ID, tasks[index].RunTaskLogId, data)
		}

		if tasks[index].Switch == "enable" {
			// 添加定时任务.
			execTime, _ := utils.StringToTime(tasks[index].ExecTime)
			_ = Cron.AddTask(tasks[index].ID, tasks[index].TimeType, execTime, tasks[index].Day)
		}
		// 月份
	}

}

func (t *cronTask) Custom(spec string, task func()) (cron.EntryID, error) {
	return t.cron.AddFunc(spec, task)
}
